/// <reference name="MicrosoftAjax.js" />
/// <reference name="dnn.controls.js" assembly="DotNetNuke.WebControls" />
/// <reference name="dnn.js" assembly="DotNetNuke.WebUtility" />
/// <reference name="dnn.dom.positioning.js" assembly="DotNetNuke.WebUtility" />
/// <reference name="dnn.xmlhttp.js" assembly="DotNetNuke.WebUtility" />

Type.registerNamespace('dnn.controls');

dnn.extend(dnn.controls, {
    initTree: function(ctl)
    {
        if (ctl)
        {
            var tree = new dnn.controls.DNNTree(ctl);
            tree.initialize();
            return tree;
        }
    }
});

//------- Constructor -------//
dnn.controls.DNNTree = function(o)
{
    dnn.controls.DNNTree.initializeBase(this, [o]);

    //--- Data Properties ---//  
    this.rootNode = null;
    this.nodes = [];
    this._loadNodes();
    this.hoverTreeNode = null;
    this.selTreeNode = null;

    //--- Appearance Properties ---//
    this.css = this.getProp('css', '');
    this.cssChild = this.getProp('csschild', '');
    this.cssHover = this.getProp('csshover', '');
    this.cssSel = this.getProp('csssel', '');
    this.cssIcon = this.getProp('cssicon', '');

    this.sysImgPath = this.getProp('sysimgpath', 'images/');
    this.imageList = this.getProp('imagelist', '').split(',');
    this.expandImg = this.getProp('expimg', '');
    this.workImg = this.getProp('workimg', 'dnnanim.gif');
    this.animf = new Number(this.getProp('animf', '5'));

    this.collapseImg = this.getProp('colimg', '');

    this.indentWidth = new Number(this.getProp('indentw', '10'));
    if (this.indentWidth == 0)
        this.indentWidth = 10;
    this.checkBoxes = this.getProp('checkboxes', '0') == '1';
    this.checkBoxMode = new Number(this.getProp('cbm', '0'));
    this.target = this.getProp('target', '');
    this.defaultJS = this.getProp('js', '');

    this.postBack = this.getProp('postback', '');
    this.callBack = this.getProp('callback', '');
    this.callBackStatFunc = this.getProp('callbackSF', '');
    if (this.callBackStatFunc.length > 0)
        this.add_handler('callBackStatus', eval(this.callBackStatFunc));

    //obtain width of expand image
    this.expImgWidth = new Number(this.getProp('expcolimgw', '12'));

    kbCtl = this.container;
    if (this.container.tabIndex <= 0)
        this.container.tabIndex = 0;
    else
    {
        kbCtl = document.createElement('input');
        kbCtl.type = 'text';
        kbCtl.style.width = 0;
        kbCtl.style.height = 0;
        kbCtl.style.background = 'transparent';
        kbCtl.style.border = 0;
        kbCtl.style.positioning = 'absolute';
        kbCtl.style.left = '-999em';
        this.container.parentNode.appendChild(kbCtl);
    }
    this.addHandlers(kbCtl, { "keydown": this.keydownHandler,
        "focus": this.focusHandler
    }, this);

    this._onsubmitDelegate = Function.createDelegate(this, this._onsubmit);
    dnn.controls.submitComp.add_handler(this._onsubmitDelegate);

}

dnn.controls.DNNTree.prototype =
{

    initialize: function()
    {
        dnn.controls.DNNTree.callBaseMethod(this, 'initialize');
        this.generateTreeHTML();
    },

    focusHandler: function(e)
    {
        var tNode = this.hoverTreeNode;
        if (tNode == null)
            tNode = new dnn.controls.DNNTreeNode(this.rootNode.childNodes(0));
        this.hoverNode(tNode);
        this.container.onfocus = null;
    },

    keydownHandler: function(e)
    {
        var dir = 0;
        var axis = '';

        if (e.keyCode == KEY_UP_ARROW)
        {
            dir = -1;
            axis = 'y';
        }
        if (e.keyCode == KEY_DOWN_ARROW)
        {
            dir = 1;
            axis = 'y';
        }
        if (e.keyCode == KEY_LEFT_ARROW)
        {
            dir = -1;
            axis = 'x';
        }
        if (e.keyCode == KEY_RIGHT_ARROW)
        {
            dir = 1;
            axis = 'x';
        }

        if (dir != 0)
        {
            var tNode = this.hoverTreeNode;
            var node;
            if (tNode == null)
                tNode = new dnn.controls.DNNTreeNode(this.rootNode.childNodes(0));

            if (axis == 'x')
            {
                if (dir == -1)
                {
                    if (tNode.hasNodes && tNode.expanded)
                        this.collapseNode(tNode);
                    else
                        node = tNode.node.parentNode();
                }

                if (dir == 1)
                {
                    if (tNode.hasNodes || tNode.hasPendingNodes)
                    {
                        if (tNode.expanded != true)
                            this.expandNode(tNode);
                        else
                            node = tNode.node.childNodes(0);
                    }
                }
            }
            else if (axis == 'y')
            {
                var iNodeIndex = tNode.node.getNodeIndex('id');
                var parentNode = tNode.node.parentNode();
                if (tNode.hasNodes && tNode.expanded && dir > 0)	//if has expanded nodes and going down, select first child
                    node = tNode.node.childNodes(0);
                else if (iNodeIndex + dir < 0)	//if first node was selected and going up, select parent
                    node = parentNode;
                else if (iNodeIndex + dir < parentNode.childNodeCount())	//if navigated index less than number of nodes contained in parent
                {
                    node = parentNode.childNodes(iNodeIndex + dir); //navigate there
                    if (dir == -1)		//if going up... look for expanded sibling above (recursively)
                    {
                        var tNode2 = new dnn.controls.DNNTreeNode(node);
                        while (tNode2.expanded)	//determine if parent node is expanded, if so find its last child node
                        {
                            if (tNode2.node.childNodeCount() == 0)
                                break;
                            node = tNode2.node.childNodes(tNode2.node.childNodeCount() - 1); //select last node in parent's collection
                            tNode2 = new dnn.controls.DNNTreeNode(node); //needed to check expanded property
                        }
                    }
                }
                else if (parentNode.nodeName() != 'root')	//logic for last node in collection
                {
                    var iNodeIndex = parentNode.getNodeIndex('id');
                    var tempParent = parentNode;
                    if (dir == 1)	//if going down... verify that parent node has sibling available to select, if not recursively look for one
                    {
                        while (tempParent.nodeName() != 'root' && iNodeIndex + dir >= tempParent.parentNode().childNodeCount())	//while index greater than node count
                        {
                            tempParent = tempParent.parentNode();
                            iNodeIndex = tempParent.getNodeIndex('id');
                        }
                    }
                    if (tempParent.nodeName() != 'root')
                        node = tempParent.parentNode().childNodes(iNodeIndex + 1);
                }
            }
            if (node != null && node.nodeName() != 'root')
                this.hoverNode(new dnn.controls.DNNTreeNode(node));

            return false;
        }

        if (e.keyCode == KEY_RETURN && this.hoverTreeNode != null)
        {
            this.selectNode(this.hoverTreeNode);
            return false;
        }

    },

    hoverNode: function(tNode)
    {
        if (this.hoverTreeNode != null)
        {
            this.hoverTreeNode.hover = false;
            this.assignCss(this.hoverTreeNode);
        }
        tNode.hover = true;
        this.assignCss(tNode);
        this.hoverTreeNode = tNode;
    },

    getXml: function()
    {
        return this.rootNode.getXml();
    },

    expandNode: function(tNode)
    {
        var ctr = this.getChildControl(tNode.id, 'pctr');
        var expandCol = this.getChildControl(tNode.id, 'expcol');

        expandCol.src = this.expandImg;
        tNode.expanded = true;
        tNode.update();
        this.update();

        if (tNode.hasPendingNodes)
        {
            var sXml = tNode.node.getXml();
            tNode.tree = this; //need to give reference back to self

            if (this.workImg != null)
            {
                var icon = this.getChildControl(tNode.id, 'icn');
                if (icon)
                    icon.src = this.sysImgPath + this.workImg;
            }
            if (this.callBack.indexOf('[NODEXML]') > -1)
                eval(this.callBack.replace('[NODEXML]', dnn.escapeForEval(sXml)));
            else
                eval(this.callBack.replace('[NODEID]', tNode.id));

            tNode.hasPendingNodes = false;
            tNode.hasNodes = true;
            this.hoverTreeNode = tNode;
        }
        else
        {
            dnn.dom.expandElement(ctr, this.animf);
        }

        return true;
    },

    collapseNode: function(tNode)
    {
        var ctr = this.getChildControl(tNode.id, 'pctr');
        var expandCol = this.getChildControl(tNode.id, 'expcol');
        //ctr.style.display = 'none';
        dnn.dom.collapseElement(ctr, this.animf);
        expandCol.src = this.collapseImg;
        tNode.expanded = null;
        tNode.update();
        this.update();
        return true;
    },

    selectNode: function(tNode)
    {
        var arg = new dnn.controls.DNNNodeEventArgs(tNode);
        this.invoke_handler('click', arg);
        if (arg.get_cancel())
            return;

        if (this.selTreeNode != null && this.checkBoxes == false)
        {
            this.selTreeNode.selected = null;
            this.assignCss(this.selTreeNode);
            this.selTreeNode.update('selected');
        }

        if (tNode.selected)
        {
            tNode.selected = null;
            this.assignCss(tNode);
        }
        else
        {
            tNode.selected = true;
            this.hoverTreeNode = tNode;
            this.assignCss(tNode);
        }
        tNode.update('selected');

        this.selTreeNode = tNode;
        this.update();

        var chk = this.getChildControl(tNode.id, 'chk');
        if (chk != null)
            chk.checked = tNode.selected;

        if (tNode.selected)
        {
            var js = '';
            if (this.defaultJS.length > 0)
                js = this.defaultJS;
            if (tNode.js.length > 0)
                js = tNode.js;

            if (js.length > 0)
            {
                if (eval(js) == false)
                    return; //don't do postback if returns false
            }

            if (tNode.clickAction == null || tNode.clickAction == dnn.controls.action.postback)
                eval(this.postBack.replace('[NODEID]', tNode.id));
            else if (tNode.clickAction == dnn.controls.action.nav)
                dnn.dom.navigate(tNode.url, tNode.target.length > 0 ? tNode.target : this.target);
            else if (tNode.clickAction == dnn.controls.action.expand)
            {
                if (tNode.hasNodes || tNode.hasPendingNodes)
                {
                    if (tNode.expanded)
                        this.collapseNode(tNode);
                    else
                        this.expandNode(tNode);
                }
            }
        }

        return true;
    },

    selectAllChildren: function(tNode)
    {
        var childTNode;
        for (var i = 0; i < tNode.childNodeCount(); i++)
        {
            childTNode = tNode.childNodes(i);
            if (childTNode.selected != tNode.selected)
                this.selectNode(childTNode);

            this.selectAllChildren(childTNode);
        }
    },

    assignCss: function(tNode)
    {
        var oText = this.getChildControl(tNode.id, 't'); //, this.container);
        var sNodeCss = this.css;

        if (tNode.level > 0 && this.cssChild.length > 0)
            sNodeCss = this.cssChild;

        if (tNode.css.length > 0)
            sNodeCss = tNode.css;

        //tNode.hoverCss;

        if (tNode.hover)
            sNodeCss += ' ' + (tNode.cssHover.length > 0 ? tNode.cssHover : this.cssHover);
        if (tNode.selected)
            sNodeCss += ' ' + (tNode.cssSel.length > 0 ? tNode.cssSel : this.cssSel);

        oText.className = sNodeCss;
    },

    update: function(force)
    {
        if (force)
        {
            if (this.selTreeNode)
                dnn.setVar(this.ns + ':selected', this.selTreeNode.id); 	//BACKWARDS COMPAT ONLY!!!    
            dnn.setVar(this.ns + '_json', this.rootNode.getJSON());
        }
        return true;
    },

    //--- Event Handlers ---//
    _onsubmit: function()
    {
        this.update(true);
    },

    callBackStatus: function(result, ctx, req)
    {
        var tNode = ctx;
        var tree = tNode.tree;
        tree.invoke_compatHandler('callBackStatus', result, ctx, req);
    },

    callBackSuccess: function(result, ctx, req)
    {
        var tNode = ctx;
        var tree = tNode.tree;
        var parent = tNode.node; ;

        var json = dnn.evalJSON("{" + result + "}");
        parent.nodes = json.nodes;
        parent.setupJSONNodes(parent.rootNode(), parent, parent.nodes);

        if (tree.workImg != null)
        {
            var icon = tree.getChildControl(tNode.id, 'icn');
            if (tNode.image != '')
                icon.src = tNode.image;
            else if (tNode.imageIndex > -1)
                icon.src = tree.imageList[tNode.imageIndex];
        }

        //ctx.tree.generateTreeHTML();	
        //get container
        var ctr = tree.getChildControl(tNode.id, 'pctr');
        tree.renderNode(tNode.node, ctr, true);

        tree.update();

        //var ctr = tree.getChildControl(tNode.id, 'pctr');
        tree.expandNode(new dnn.controls.DNNTreeNode(tNode.node));

        tree.invoke_compatHandler('callBackStatus', result, ctx, req);
        tree.invoke_handler('callBackSuccess', new dnn.controls.DNNCallbackEventArgs(result, ctx, req));
    },

    callBackFail: function(result, ctx, req)
    {
        var tNode = ctx;
        var tree = tNode.tree;
        tree.invoke_handler('callBackFail', new dnn.controls.DNNCallbackEventArgs(result, ctx, req));
    },

    nodeExpColClick: function(evt)
    {
        var node = this._findEventNode(evt);
        if (node != null)
        {
            var tNode = new dnn.controls.DNNTreeNode(node);

            var ctr = this.getChildControl(tNode.id, 'pctr');
            if (tNode.expanded)
                this.collapseNode(tNode);
            else
                this.expandNode(tNode);
        }
    },

    nodeCheck: function(evt)
    {
        var node = this._findEventNode(evt);
        if (node != null)
        {
            var tNode = new dnn.controls.DNNTreeNode(node);
            if (this.checkBoxMode == 2 && this.selTreeNode != null)
                this.selectNode(this.selTreeNode);   //unselect node

            this.selectNode(tNode);

            if (this.checkBoxMode == 1)
                this.selectAllChildren(tNode);

        }
    },

    nodeTextClick: function(evt)
    {
        var node = this._findEventNode(evt);
        if (node != null)
        {
            this.selectNode(new dnn.controls.DNNTreeNode(node));
        }
    },

    nodeTextMOver: function(evt)
    {
        var node = this._findEventNode(evt);
        if (node != null)
            this.hoverNode(new dnn.controls.DNNTreeNode(node));
    },

    nodeTextMOut: function(evt)
    {
        var node = this._findEventNode(evt);
        if (node != null)
            this.assignCss(new dnn.controls.DNNTreeNode(node));
    },

    dispose: function()
    {
        this._onsubmitDelegate = null;
        dnn.controls.DNNTree.callBaseMethod(this, 'dispose');
    },

    //--- Generates tree HTML through passed in XML DOM ---//
    generateTreeHTML: function()
    {
        //this.debugWrite('generateTreeHTML', 'generateTreeHTML');
        //this.rootNode = this.DOM.rootNode();
        this.renderNode(null, this.container);
        //this.debugWrite('generateTreeHTML', 'generateTreeHTML [END]');	
    },

    renderNode: function(node, oCont, bExists)
    {
        var oChildCont = oCont;
        var tNode;

        if (bExists != true)
        {
            if (node != null)
            {
                //render node
                tNode = new dnn.controls.DNNTreeNode(node);
                var oNewContainer;
                oNewContainer = this.createChildControl('DIV', tNode.id, 'ctr'); //container for Node
                oNewContainer.appendChild(this.renderSpacer((this.indentWidth * tNode.level) + ((tNode.hasNodes || tNode.hasPendingNodes) ? 0 : this.expImgWidth))); //indent node
                if (tNode.hasNodes || tNode.hasPendingNodes)	//if node has children then render expand/collapse icon
                    oNewContainer.appendChild(this.renderExpCol(tNode));

                if (this.checkBoxes)
                    oNewContainer.appendChild(this.renderCheckbox(tNode));

                var oIconCont = this.renderIconCont(tNode);
                oNewContainer.appendChild(oIconCont);
                if (tNode.imageIndex > -1 || tNode.image != '')	//if node has image 
                {
                    oIconCont.appendChild(this.renderIcon(tNode));
                    //oNewContainer.appendChild(this.renderSpacer(10));
                }
                //else
                //    oIconCont.appendChild(this.renderSpacer(this.indentWidth));

                oNewContainer.appendChild(this.renderText(tNode)); //render text
                oCont.appendChild(oNewContainer);
                this.assignCss(tNode);
            }
            else
                node = this.rootNode;

            if (tNode != null && (tNode.hasNodes || tNode.hasPendingNodes))	//if node has children render container and hide if necessary
            {
                oChildCont = this.createChildControl('DIV', tNode.id, 'pctr'); //Not using SPAN due to FireFox bug...
                if (tNode.expanded != true)
                    oChildCont.style.display = 'none';
                oCont.appendChild(oChildCont);
            }
        }

        for (var i = 0; i < node.childNodeCount(); i++)	//recursively call child rendering
            this.renderNode(node.childNodes(i), oChildCont);


    },

    renderExpCol: function(tNode)
    {
        var img = this.createChildControl('IMG', tNode.id, 'expcol');
        if ((tNode.hasNodes || tNode.hasPendingNodes) && this.expandImg.length)
        {
            if (tNode.expanded)
                img.src = this.expandImg;
            else
                img.src = this.collapseImg;
        }
        //img.style.width = this.expImgWidth;	
        //img.style.cursor = 'hand';	//ie
        img.style.cursor = 'pointer';
        //dnn.dom.attachEvent(img, 'onclick', this.nodeExpColClick);
        //dnn.dom.attachEvent(img, 'onclick', dnn.dom.getObjMethRef(this, 'nodeExpColClick'));
        //img.onclick = dnn.dom.getObjMethRef(this, 'nodeExpColClick');
        this.addHandlers(img, { "click": this.nodeExpColClick }, this);

        return img;
    },

    renderIconCont: function(tNode)
    {
        var span = this.createChildControl('SPAN', tNode.id, 'icnc');
        if (tNode.cssIcon.length > 0)
            span.className = tNode.cssIcon;
        else if (this.cssIcon.length > 0)
            span.className = this.cssIcon;

        return span;
    },

    renderIcon: function(tNode)
    {
        var img = this.createChildControl('IMG', tNode.id, 'icn');
        if (tNode.image != '')
            img.src = tNode.image;
        else if (tNode.imageIndex > -1)
            img.src = this.imageList[tNode.imageIndex];
        //img.style.paddingRight = 10;	//doesn't work in IE???
        return img;
    },

    renderCheckbox: function(tNode)
    {
        var chk = this.createChildControl('INPUT', tNode.id, 'chk');
        chk.type = 'checkbox';
        chk.defaultChecked = tNode.selected;
        chk.checked = tNode.selected;

        this.addHandlers(chk, { "click": this.nodeCheck }, this);
        return chk;
    },

    renderSpacer: function(width)
    {
        var img = document.createElement('IMG');
        img.src = this.sysImgPath + 'spacer.gif';
        img.width = width;
        img.height = 1;
        img.style.width = width + 'px';
        img.style.height = '1px';
        return img;
    },

    renderText: function(tNode)
    {
        var span = this.createChildControl('SPAN', tNode.id, 't');
        span.innerHTML = tNode.text;
        span.style.cursor = 'pointer';

        if (tNode.toolTip.length > 0)
            span.title = tNode.toolTip;

        if (tNode.enabled || tNode.clickAction == dnn.controls.action.expand)
        {
            //span.onclick = dnn.dom.getObjMethRef(this, 'nodeTextClick');
            if (this.checkBoxes)
                this.addHandlers(span, { "click": this.nodeCheck }, this);
            else
                this.addHandlers(span, { "click": this.nodeTextClick }, this);

            if (this.cssHover.length > 0)	//only do this if necessary
            {
                //span.onmouseover = dnn.dom.getObjMethRef(this, 'nodeTextMOver');
                //span.onmouseout = dnn.dom.getObjMethRef(this, 'nodeTextMOut');
                this.addHandlers(span, { "mouseover": this.nodeTextMOver,
                    "mouseout": this.nodeTextMOut
                }, this);
            }
        }

        if (tNode.selected)
        {
            this.selTreeNode = tNode;
            this.hoverTreeNode = tNode;
        }

        return span;
    },

    _findEventNode: function(evt)
    {
        return this.rootNode.findNode(this.getChildControlBaseId(evt.target));
    },

    _loadNodes: function()
    {
        var json = dnn.evalJSON(dnn.getVar(this.ns + '_json'));
        if (json)
        {
            this.nodes = json.nodes;
            this.rootNode = {};
            this.rootNode.nodes = this.nodes;
            this.rootNode.id = this.ns;
            this.rootNode = new dnn.controls.JSONNode(this.rootNode, 'root', 0);
        }
    }


    /*get_DebugMode: function() {return this._debugMode;},
    set_DebugMode: function(value) 
    {
    this._debugMode = value;
    if (this._debugMode && this._debugControl == null)
    {
    this._debugControl = document.createElement('textarea');
    this._debugControl.rows = 20;
    this._debugControl.cols = 150;
    document.forms[0].appendChild(this._debugControl);
    }
    else if (this._debugControl)
    {
    this._debugControl.style.display = 'none';
    }
    },

_debugWrite: function()
    {
    this._debugControl.value += this._debugBuffer;
    this._debugBuffer = '';
    },

debugWrite: function(category, text)
    {
    if (this._debugMode)
    {
    if (this._debugControl == null)
    {
    this.set_DebugMode(this._debugMode);
    }
    
    var s = text;
    var d = new Date();
    if (this._debugTimes[category] && text.indexOf('[END]') > -1)
    {
    s += ' (' + ((d - this._debugTimes[category])/ 1000) + ')'; 
    }
    this._debugTimes[category] = d;
    this._debugBuffer += s + '\n';
    if (this._prevDebugTimeout)
    {
    window.clearTimeout(this._prevDebugTimeout);
    }
    this._prevDebugTimeout = window.setTimeout(Function.createDelegate(this, this._debugWrite), 1000);
    }
    }*/

} //END DNNTree
    dnn.controls.DNNTree.registerClass('dnn.controls.DNNTree', dnn.controls.control);

    dnn.controls.DNNTreeNode = function(node)
    {
        dnn.controls.DNNTreeNode.initializeBase(this, [node]);

        //tree specific attributes
        this.hover = false;
        this.expanded = node.getAttribute('expanded', '0') == '1' ? true : null;
        this.selected = node.getAttribute('selected', '0') == '1' ? true : null;
        this.clickAction = node.getAttribute('ca', dnn.controls.action.postback);
        this.imageIndex = new Number(node.getAttribute('imgIdx', '-1')); //defaulting to 0 for backwards compat!

        //this.checkBox = node.getAttribute('checkBox', '0');	//IS THIS NECESSARY?
    }

    //DNNTreeNode specific methods
    dnn.controls.DNNTreeNode.prototype =
{
    childNodes: function(iIndex)
    {
        if (this.node.childNodes(iIndex) != null)
            return new dnn.controls.DNNTreeNode(this.node.childNodes(iIndex));
    }
}
    dnn.controls.DNNTreeNode.registerClass('dnn.controls.DNNTreeNode', dnn.controls.DNNNode);


    //BACKWARDS COMPAT ONLY!
    var DT_ACTION_POSTBACK = 0;
    var DT_ACTION_EXPAND = 1;
    var DT_ACTION_NONE = 2;
    var DT_ACTION_NAV = 3;

    function __dt_DNNTreeNode(ctl)
    {
        var node = dnn.controls.controls[ctl.ns].rootNode.findNode(ctl.nodeid);
        if (node != null)
        {
            var tNode = new dnn.controls.DNNTreeNode(node);

            this.ctl = ctl;
            this.id = ctl.id;
            this.key = tNode.key;
            this.nodeID = ctl.nodeid; //trim off t
            this.text = tNode.text;
            this.serverName = ctl.name;
        }
    }


